package kuzha.code.jvm;

import static kuzha.code.jvm.CodeGenUtils.emitIntValue;
import static kuzha.code.jvm.CodeGenUtils.emitNumber;
import static org.objectweb.asm.Opcodes.AASTORE;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ANEWARRAY;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.PUTSTATIC;
import kuzha.runtime.AsPattern;
import kuzha.runtime.BooleanPattern;
import kuzha.runtime.CasePattern;
import kuzha.runtime.ListPattern;
import kuzha.runtime.NullPattern;
import kuzha.runtime.NumberPattern;
import kuzha.runtime.StringPattern;
import kuzha.runtime.StructPattern;
import kuzha.runtime.VariablePattern;
import kuzha.runtime.VoidPattern;
import kuzha.runtime.WildCardPattern;

import org.objectweb.asm.MethodVisitor;

public class CasePatternArrayPool extends AbstractPool<CasePattern[]> {
	public CasePatternArrayPool(String fieldsPrefix, String systemName) {
		super(fieldsPrefix, systemName, "[Lkuzha/runtime/CasePattern;");
	}
	
	@Override
	protected void emitInitializeConstant(MethodVisitor mv, String fieldname, CasePattern[] obj) {
		emitIntValue(mv, obj.length);
		mv.visitTypeInsn(ANEWARRAY, "kuzha/runtime/CasePattern");
		for(int i = 0; i < obj.length; i++) {
			mv.visitInsn(DUP);
			emitIntValue(mv, i);
			emitCasePattern(mv, obj[i]);
			mv.visitInsn(AASTORE);
		}
		mv.visitFieldInsn(PUTSTATIC, systemName, fieldname, typeName);
	}
	
	private void emitCasePattern(MethodVisitor mv, CasePattern pat) {
		if (pat != null) {
			if (pat instanceof WildCardPattern) {
				mv.visitFieldInsn(GETSTATIC, "kuzha/runtime/WildCardPattern", "INSTANCE", "Lkuzha/runtime/WildCardPattern;");
			}
			else if (pat instanceof VariablePattern) {
				mv.visitLdcInsn(((VariablePattern)pat).name);
				mv.visitMethodInsn(INVOKESTATIC, "kuzha/runtime/VariablePattern", "valueOf", "(Ljava/lang/String;)Lkuzha/runtime/VariablePattern;");
			}
			else if (pat instanceof StringPattern) {
				mv.visitLdcInsn(((StringPattern)pat).value);
				mv.visitMethodInsn(INVOKESTATIC, "kuzha/runtime/StringPattern", "valueOf", "(Ljava/lang/String;)Lkuzha/runtime/StringPattern;");
			}
			else if (pat instanceof NumberPattern) {
				emitNumber(mv, ((NumberPattern)pat).value);
				mv.visitMethodInsn(INVOKESTATIC, "kuzha/runtime/NumberPattern", "valueOf", "(Ljava/lang/Number;)Lkuzha/runtime/NumberPattern;");
			}
			else if (pat instanceof BooleanPattern) {
				if (((BooleanPattern)pat).value)
					mv.visitFieldInsn(GETSTATIC, "kuzha/runtime/BooleanPattern", "TRUE", "Lkuzha/runtime/BooleanPattern;");
				else
					mv.visitFieldInsn(GETSTATIC, "kuzha/runtime/BooleanPattern", "FALSE", "Lkuzha/runtime/BooleanPattern;");
			}
			else if (pat instanceof NullPattern) {
				mv.visitFieldInsn(GETSTATIC, "kuzha/runtime/NullPattern", "INSTANCE", "Lkuzha/runtime/NullPattern;");
			}
			else if (pat instanceof VoidPattern) {
				mv.visitFieldInsn(GETSTATIC, "kuzha/runtime/VoidPattern", "INSTANCE", "Lkuzha/runtime/VoidPattern;");
			}
			else if (pat instanceof StructPattern) {
				StructPattern sp = (StructPattern) pat;
				mv.visitLdcInsn(sp.name);
				emitIntValue(mv, sp.pats.length);
				mv.visitTypeInsn(ANEWARRAY, "kuzha/runtime/CasePattern");
				for(int i = 0; i < sp.pats.length; i++) {
					mv.visitInsn(DUP);
					emitIntValue(mv, i);
					emitCasePattern(mv, sp.pats[i]);
					mv.visitInsn(AASTORE);
				}
				emitCasePattern(mv, sp.tail);
				mv.visitMethodInsn(INVOKESTATIC, "kuzha/runtime/StructPattern", "valueOf", "(Ljava/lang/String;[Lkuzha/runtime/CasePattern;Lkuzha/runtime/CasePattern;)Lkuzha/runtime/StructPattern;");
			}
			else if (pat instanceof ListPattern) {
				ListPattern lp = (ListPattern) pat;
				emitIntValue(mv, lp.pats.length);
				mv.visitTypeInsn(ANEWARRAY, "kuzha/runtime/CasePattern");
				for(int i = 0; i < lp.pats.length; i++) {
					mv.visitInsn(DUP);
					emitIntValue(mv, i);
					emitCasePattern(mv, lp.pats[i]);
					mv.visitInsn(AASTORE);
				}
				emitCasePattern(mv, lp.tail);
				mv.visitMethodInsn(INVOKESTATIC, "kuzha/runtime/ListPattern", "valueOf", "([Lkuzha/runtime/CasePattern;Lkuzha/runtime/CasePattern;)Lkuzha/runtime/ListPattern;");
			}
			else if (pat instanceof AsPattern) {
				AsPattern as = (AsPattern) pat;
				mv.visitLdcInsn(as.name);
				emitCasePattern(mv, as.pat);
				mv.visitMethodInsn(INVOKESTATIC, "kuzha/runtime/AsPattern", "valueOf", "(Ljava/lang/String;Lkuzha/runtime/CasePattern;)Lkuzha/runtime/AsPattern;");
			}
			else {
				throw new IllegalArgumentException("unknown objects: " + pat);
			}
		}
		else {
			mv.visitInsn(ACONST_NULL);
		}
	}
}
